'use strict'

const http = require('http');
const fs = require('fs');

const fsp = fs.promises//异步接口的封装

/**------------------------解析Body数据--------------------- */

/**
 * Content-Disposition:form-data;name = "file";
 * 
 * @param {string} headerDate 
 */

function parseBodyHeaders(headerDate) {
    let hlist = headerDate.split('\r\n').filter(p => p.length > 0)

    //解析Content-Disposition => hlist[0]
    //按照；切分数组，选出从第二个元素开始的数组
    let csplit = hlist[0].split('; ').slice(1)

    let filename
    let name

    for (let a of csplit) {
        if (a.indexOf('filename=') === 0) {
            filename = a.substring(a.indexOf('=') + 2, a.length - 1)
        }
        else {
            name = a.substring(a.indexOf('=') + 2, a.length - 1)
        }
    }
    let headers = {}
    let ind = 0;
    let k
    for (let h of hlist) {
        ind = h.indexOf(':')
        k = h.substring(0, ind).toLowerCase()
        headers[k] = h.substring(ind + 1).trim()

    }
    return {
        filename,
        name,
        headers,
        contentType: headers['content-type'] || []
    }

    let contentType
}

function parseBody(bodyData, headers) {
    let ctype = headers['content-type']

    let bdy = ctype.substring(ctype.indexOf('=') + 1)

    let crlf_bdy = `\r\n--${bdy}`

    let header_end_index = bodyData.indexOf('\r\n\r\n', crlf_bdy.length)

    let headerDate = bodyData.toString('utf8', crlf_bdy.length, header_end_index);


    //解析文件头信息
    let hd = parseBodyHeaders(headerDate)

    let file = {
        start: header_end_index + 4,
        end: bodyData.indexOf(crlf_bdy, header_end_index),
        ...hd
    }

    file.length = file.end - file.start

    return file
}

function parseExtName (filename){
    if(filename.length<2){
        return ''
    }

    let nsplit = filename.split('.').filter(p => p.length >0)
    if(nsplit.length<2)return''
    return `.${nsplit[nsplit.length -1]}`
}
/**------------------------解析Body数据-END--------------------- */

let html = `
    <!DOCTYPE html>
    <html>
    <head><title>first http</title>
    </head>
    <body>你好。</body>
    </html>
`


let routerTable = {

    GET: {
        '/': async (ctx) => {
            ctx.response.setHeader('content-type', 'text/html;charset=utf-8');
            ctx.end(html);
        },

        '/test': async (ctx) => {
            let status = parseInt(Math.random() * 200) + 199
            ctx.response.statusCode = status
            ctx.end('test end')

        },
        '/upload': async (ctx) => {
            let stm = fs.createReadStream('./upload.html')
            stm.pipe(ctx.response, { end: false })
            stm.on('end', () => {
                ctx.end()
            })

        },
    },
    POST: {
        '/upload': async (ctx) => {
            let file = parseBody(ctx.rawBody, ctx.headers)
            console.log(file)

            if (!ctx.rawBody) {
                ctx.end();
                return;
            }
            let fname = `${Date.now()}-${Math.random()}`+`${parseExtName(file.filename)}`
            let fh = await fsp.open(fname, 'w+')
            let fret = await fh.write(ctx.rawBody,
                file.start, file.length, 0);
            fh.close();
            ctx.end(`${fret.bytesWritten}`);
        }

    }
}





http.createServer(async (request, response) => {

    let rm = routerTable[request.method]

    let usplit = request.url.split('?');

    let pathname = usplit[0]
    let querystring = usplit[1] || ''

    if (!rm || !rm[pathname]) {
        response.statusCode = 404
        response.end('page not found')
        return
    }
    //ctx是context的缩写，意味请求上下文，就是一个打包了各种数据和方法的对象
    let ctx = {
        method: request.method,
        path: pathname,
        search: querystring,
        headers: request.headers,
        request: request,
        response: response,
        end: (data, encoding = 'utf8') => {
            response.end(data, encoding)
        },
        write: (data, encoding = 'utf8') => {
            response.end(data, encoding)
        },
        totalLength: 0,
        rawBody: null,
        bufferList: []
    }

    await new Promise((rv, rj) => {
        if ('PD'.indexOf(request.method[0]) >= 0) {
            request.on('data', chunk => {
                ctx.bufferList.push(chunk)
                ctx.totalLength += chunk.length
            })
        } else {
            request.on('data', data => {
                request.statusCode = 400
                request.end('bad resquest', () => {
                    request.destroy()
                })
            })
        }
        let resolved = false;
        request.on('end', () => {
            resolved = true;
            rv();
        })
        request.on('close', () => {
            (!resolved) && rv();
        })
    })


    if (!response.writable || response.writableEnded) {
        return
    }
    if (ctx.bufferList.length > 0) {
        ctx.rawBody = Buffer.concat(ctx.bufferList, ctx.totalLength)
        ctx.bufferList = []
    }
    rm[pathname](ctx)

})
    .listen(3456);